#include "http.h"
#include "define.h"

static char *url;
static char *login_url;
static char *g_psSubDeviceAuthUrl = NULL;              //数盾前置服务子设备验签URL
extern char *LOGIN_ZONE;
extern char *LOGIN_NODE;
extern int IS_HTTPS;
extern ob_context g_mqtt_ob;
extern char *SD_ADDR_PORT;
extern char *SD_FRONT_PORT;

static ProgressInfo *progress_info[20];
ProgressInfo **progress_info_ptr = progress_info;


// 获取证书主题
char* x509_get_subject(X509 *cert) {
    if (cert == NULL) return NULL;
    return X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
}

// 获取证书颁发者
char* x509_get_issuer(X509 *cert) {
    if (cert == NULL) return NULL;
    return X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
}

// 获取证书序列号
char* x509_get_serial(X509 *cert) {
    if (cert == NULL) return NULL;
    
    ASN1_INTEGER *serial = X509_get_serialNumber(cert);
    BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL);
    if (bn == NULL) return NULL;
    
    char *serial_str = BN_bn2dec(bn);
    BN_free(bn);
    
    return serial_str;
}

char* x509_crl_to_pem_string(X509_CRL *crl) {
    if (crl == NULL) {
        return NULL;
    }
    
    BIO *bio = BIO_new(BIO_s_mem());
    if (bio == NULL) {
        return NULL;
    }
    
    // 将 X509_CRL 写入 BIO 为 PEM 格式
    if (!PEM_write_bio_X509_CRL(bio, crl)) {
        BIO_free(bio);
        return NULL;
    }
    
    // 获取 BIO 中的内容长度
    long len = BIO_get_mem_data(bio, NULL);
    if (len <= 0) {
        BIO_free(bio);
        return NULL;
    }
    
    // 分配内存并复制内容
    char *pem_str = (char*)malloc(len + 1);
    if (pem_str == NULL) {
        BIO_free(bio);
        return NULL;
    }
    
    BIO_read(bio, pem_str, len);
    pem_str[len] = '\0'; // 添加字符串终止符
    
    BIO_free(bio);
    return pem_str;
}


char* x509_to_pem_string(X509 *cert) {
    if (cert == NULL) {
        return NULL;
    }
    
    BIO *bio = BIO_new(BIO_s_mem());
    if (bio == NULL) {
        return NULL;
    }
    
    // 将 X509 证书写入 BIO 为 PEM 格式
    if (!PEM_write_bio_X509(bio, cert)) {
        BIO_free(bio);
        return NULL;
    }
    
    // 获取 BIO 中的内容长度
    long len = BIO_get_mem_data(bio, NULL);
    if (len <= 0) {
        BIO_free(bio);
        return NULL;
    }
    
    // 分配内存并复制内容
    char *pem_str = (char*)malloc(len + 1);
    if (pem_str == NULL) {
        BIO_free(bio);
        return NULL;
    }
    
    BIO_read(bio, pem_str, len);
    pem_str[len] = '\0'; // 添加字符串终止符
    
    BIO_free(bio);
    return pem_str;
}

CURLcode sslFunctionDefault(CURL *curl, void *sslCtx, void *param)
{

    return 0;
}














/**
 * @description: http初始化
 * @return {*}成功返回0，失败返回-1
 */
int http_init()
{
    // 初始化curl库
    curl_global_init(CURL_GLOBAL_DEFAULT);
    url = malloc(512);
    login_url = malloc(512);
    g_psSubDeviceAuthUrl = malloc(512);

    if (LOGIN_ADDR != NULL && LOGIN_PORT != 0)
    {
        if (IS_HTTPS == 0)
        {
            if (g_mqtt_ob.clientInfo.is_ipv6 == 1)
            {
                sprintf(login_url, "http://[%s]:%d", LOGIN_ADDR, LOGIN_PORT);
            }
            else
            {
                sprintf(login_url, "http://%s:%d", LOGIN_ADDR, LOGIN_PORT);
            }
        }
        else
        {
            if (g_mqtt_ob.clientInfo.is_ipv6 == 1)
            {
                sprintf(login_url, "https://[%s]:%d", LOGIN_ADDR, LOGIN_PORT);
            }
            else
            {
                sprintf(login_url, "https://%s:%d", LOGIN_ADDR, LOGIN_PORT);
            }
        }
        dzlog_info("login_url: %s", login_url);
    }
    else
    {
        curl_global_cleanup();
        dzlog_error("ini option is null");
        return -1;
    }

    if (FILE_SERVER_IP != NULL && FILE_SERVER_PORT != 0)
    {
        if (IS_HTTPS == 0)
        {
            if (g_mqtt_ob.clientInfo.is_ipv6 == 1)
            {
                sprintf(url, "http://[%s]:%d", FILE_SERVER_IP, FILE_SERVER_PORT);
            }
            else
            {
                sprintf(url, "http://%s:%d", FILE_SERVER_IP, FILE_SERVER_PORT);
            }
        }
        else
        {
            if (g_mqtt_ob.clientInfo.is_ipv6 == 1)
            {
                sprintf(url, "https://[%s]:%d", FILE_SERVER_IP, FILE_SERVER_PORT);
            }
            else
            {
                sprintf(url, "https://%s:%d", FILE_SERVER_IP, FILE_SERVER_PORT);
            }
        }
        dzlog_info("url: %s", url);
    }
    else
    {
        curl_global_cleanup();
        dzlog_error("ini option is null");
        return -1;
    }
    dzlog_debug("http init success");
    return 0;
}

/**
 * @description: http初始化
 * @return {*}成功返回0，失败返回-1
 */
int http_init_url(char* ip, int SSL)
{
    if (SSL == 3)
    {
        HTTP_SSL = 2;
    }
    else
    {
        HTTP_SSL = 0;
    }
    //else
    //{
    //    HTTP_SSL = SSL;
    //}
    
    dzlog_debug("http_init_url start HTTP_SSL[%d]", HTTP_SSL);
    if (LOGIN_PORT != 0)
    {
        LOGIN_ADDR = ip;
        if (IS_HTTPS == 0)
        {
            if (g_mqtt_ob.clientInfo.is_ipv6 == 1)
            {
                sprintf(login_url, "http://[%s]:%d", ip, LOGIN_PORT);
                sprintf(url, "http://[%s]:%d", ip, LOGIN_PORT);
            }
            else
            {
                sprintf(login_url, "http://%s:%d", ip, LOGIN_PORT);
                sprintf(url, "http://%s:%d", ip, LOGIN_PORT);
            }

            memset(g_psSubDeviceAuthUrl,0,512);
            dzlog_warn("http_init_url:subdevice auth url set empty!");
        }
        else
        {
            if (g_mqtt_ob.clientInfo.is_ipv6 == 1)
            {
                sprintf(login_url, "https://[%s]:%d", ip, LOGIN_PORT);
                sprintf(url, "https://[%s]:%d", ip, LOGIN_PORT);
            }
            else
            {
                sprintf(login_url, "https://%s:%d", ip, LOGIN_PORT);
                sprintf(url, "https://%s:%d", ip, LOGIN_PORT);
            }

            sprintf(g_psSubDeviceAuthUrl,"https://%s:%s/frontsystem/verifySign",ip,SD_FRONT_PORT);
            dzlog_info("http_init_url:subdevice auth url:%s", g_psSubDeviceAuthUrl);
        }

        if (SSL == 3) {
            sprintf(login_url, "https://%s:%s/ccc", ip, SD_ADDR_PORT);
            //strcat(login_url, "/ccc");
            sprintf(url, "%s", login_url);
        }
        dzlog_debug("http_init_url[%s]", login_url);

    }
    else
    {
        dzlog_debug("http_init_url LOGIN_PORT==0");
        return -1;
    }
    dzlog_debug("http init success");
    return 0;
}

/**
 * @description: http释放
 * @return {*}
 */
void http_free()
{
    free(url);
    free(login_url);
    free(g_psSubDeviceAuthUrl);
    curl_global_cleanup();
    dzlog_debug("http free success");
}

/**
 * @description: 用户密码模式登录
 * @param {char} **access_token 返回token
 * @param {char} *user 用户名
 * @param {char} *password 用户密码
 * @param {char} *clientuser 客户端名称
 * @param {char} *clientpw 客户端密码
 * @return {*} 成功返回1，失败返回0
 */
int httpFileServerLoginWithUser_v1(ST_IOTA_LOGIN_RES_V1 *login, const char *user, const char *password, const char *clientuser, const char *clientpw)
{
    // 创建一个 curl_httppost 结构体，用于存储 form-data 数据
    struct curl_httppost *body = NULL;
    struct curl_httppost *lastptr = NULL;

    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;

    bool error = false;
    if (login_url)
    {
        char *address = malloc(strlen(login_url) + strlen("/iot-file-service/token\0") + 1);
        sprintf(address, "%s%s", login_url, "/iot-file-service/token\0");
        // 添加一个字段（例如，文本字段）

        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "grantType", CURLFORM_COPYCONTENTS, "password", CURLFORM_END);
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "username", CURLFORM_COPYCONTENTS, user, CURLFORM_END);
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "password", CURLFORM_COPYCONTENTS, password, CURLFORM_END);
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "clientId", CURLFORM_COPYCONTENTS, clientuser, CURLFORM_END);
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "clientSecret", CURLFORM_COPYCONTENTS, clientpw, CURLFORM_END);
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "locale", CURLFORM_COPYCONTENTS, "zh-cn", CURLFORM_END);
        dzlog_debug("httpFileServerLoginWithUser address = %s", address);
        CURLcode code = curl_post_req(address, body, &oDataChunk, NULL, 3, false, NULL, false, NULL);
        dzlog_debug("httpFileServerLoginWithUser code = %d", code);

        if (code != CURLE_OK)
        {
            dzlog_error("httpFileServerLoginWithUser_v1 error ");
            dzlog_error("httpFileServerLoginWithUser_v1 error %s", curl_easy_strerror(code));
            // dzlog_error("请求出错：%s", curl_easy_strerror(code));
            error = true;
        }
        else
        {
            if (oDataChunk.rescode != 200)
            {
                dzlog_error("返回出错：%s", oDataChunk.memory);
                error = true;
            }
            else
            {
                dzlog_debug("resbody = %s size = %d", oDataChunk.memory, oDataChunk.size);
                if (JsonToStruct_Login(oDataChunk.memory, login) == true)
                {
                    dzlog_debug("httpFileServerLoginWithUser_v1 succ");
                }
                else
                {
                    dzlog_error("httpFileServerLoginWithUser_v1 error");
                    error = true;
                }
                // free(login.access_token);
                // free(login.client_id);
                // free(login.refresh_token);
                // free(login.short_token);
                // free(login.scope);
                // free(login.token_type);
            }
        }
        free(address);
        free(oDataChunk.memory);
        curl_formfree(body);
        if (error)
        {
            return -1;
        }
    }
    else
    {
        dzlog_error("url is null");
        return -1;
    }
    return 0;
}

int httpFileServerLoginWithUser(char *access_token, const char *user, const char *password, const char *clientuser, const char *clientpw)
{
    // 创建一个 curl_httppost 结构体，用于存储 form-data 数据
    struct curl_httppost *body = NULL;
    struct curl_httppost *lastptr = NULL;

    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;

    bool error = false;
    if (login_url)
    {

        char *address = malloc(strlen(login_url) + strlen("/api/oauth/token?grant_type=password\0") + 1);
        sprintf(address, "%s%s", login_url, "/api/oauth/token?grant_type=password\0");
        // 添加一个字段（例如，文本字段）
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "username", CURLFORM_COPYCONTENTS, user, CURLFORM_END);
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "password", CURLFORM_COPYCONTENTS, password, CURLFORM_END);
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "client_id", CURLFORM_COPYCONTENTS, clientuser, CURLFORM_END);
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "client_secret", CURLFORM_COPYCONTENTS, clientpw, CURLFORM_END);

        CURLcode code = curl_post_req(address, body, &oDataChunk, NULL, 3, false, NULL, false, NULL);
        if (code != CURLE_OK)
        {
            dzlog_error("请求出错：%s", curl_easy_strerror(code));
            error = true;
        }
        else
        {
            if (oDataChunk.rescode != 200)
            {
                dzlog_error("返回出错：%s", oDataChunk.memory);
                error = true;
            }
            else
            {
                dzlog_debug("resbody = %s size = %d", oDataChunk.memory, oDataChunk.size);
                ST_IOTA_LOGIN_RES login;
                if (JsonToStruct_Login(oDataChunk.memory, &login))
                {
                    strcpy(access_token, login.access_token);
                }
                else
                {
                    dzlog_error("解析出错");
                    error = true;
                }

                free(login.access_token);
                free(login.client_id);
                free(login.refresh_token);
                free(login.short_token);
                free(login.scope);
                free(login.token_type);
            }
        }
        free(address);
        free(oDataChunk.memory);
        curl_formfree(body);
        if (error)
        {
            return -1;
        }
    }
    else
    {
        dzlog_error("url is null");
        return -1;
    }
    return 0;
}

/**
 * @description: auth认证
 * @param {char} *access_token
 * @param {ST_IOTA_AUTH_RES} *res
 * @return {*}
 */
int httpFileServerGetHeader(char *access_token, ST_IOTA_AUTH_RES *res)
{
    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;
    bool error = false;
    if (url)
    {
        char *address = malloc(strlen(url) + strlen("/api/logs/UploadFileApiService/getFileServerHeaderInfo?_=1704177413769") + 1);
        sprintf(address, "%s%s", url, "/api/logs/UploadFileApiService/getFileServerHeaderInfo?_=1704177413769");

        CURLcode code = curl_post_req(address, NULL, &oDataChunk, NULL, 0, true, access_token, false, NULL);
        if (code != CURLE_OK)
        {
            dzlog_error("请求出错：%s", curl_easy_strerror(code));
            error = true;
        }
        else
        {
            dzlog_debug("resbody = %s", oDataChunk.memory);
            if (oDataChunk.rescode != 200)
            {
                dzlog_error("返回出错：%s", oDataChunk.memory);
                error = true;
            }
            else
            {

                if (JsonToStruct_LoginAuth(oDataChunk.memory, res))
                {
                    dzlog_debug("解析成功");
                }
                else
                {
                    dzlog_error("解析出错");
                    error = true;
                }
            }
        }
        free(oDataChunk.memory);
        free(address);
        if (error)
        {
            return -1;
        }
    }
    else
    {
        dzlog_error("url is null");
        return -1;
    }
    return 0;
}
int httpFileServerGetHeader_v1(char *access_token, ST_IOTA_AUTH_RES *res)
{
    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;
    bool error = false;
    if (url)
    {
        char urltmp[100];
        sprintf(urltmp, "/iot-file-service/getFileServerHeaderInfo?zoneCode=%s&nodeCode=%s", LOGIN_ZONE, LOGIN_NODE);
        dzlog_debug("httpFileServerGetHeader_v1 urltmp = %s", urltmp);
        char *address = malloc(strlen(url) + strlen(urltmp) + 1);
        sprintf(address, "%s%s", url, urltmp);
        dzlog_info("httpFileServerGetHeader_v1 address = %s", address);
        // char* header1 = malloc(strlen(header->accessKey) + strlen(header->timestamp) + strlen(header->sign) + strlen(header->nonce) + 100);
        // sprintf(header1, "accessKey:%s|timestamp:%s|sign:%s|nonce:%s\0", header->accessKey, header->timestamp, header->sign, header->nonce);

        int code = curl_get_req(address, &oDataChunk, NULL, false, NULL, false, true, access_token, NULL);
        dzlog_debug("httpFileServerGetHeader_v1 11");
        dzlog_debug("httpFileServerGetHeader_v1 22 ");
        if (code != CURLE_OK)
        {
            dzlog_error("httpFileServerGetHeader_v1 error:%s", curl_easy_strerror(code));
            error = true;
        }
        else
        {
            dzlog_debug("httpFileServerGetHeader_v1 resbody = %s", oDataChunk.memory);
            if (oDataChunk.rescode != 200)
            {
                dzlog_error("httpFileServerGetHeader_v1 返回出错：%s", oDataChunk.memory);
                error = true;
            }
            else
            {

                if (JsonToStruct_LoginAuth(oDataChunk.memory, res))
                {
                    dzlog_info("httpFileServerGetHeader_v1 解析成功");
                }
                else
                {
                    dzlog_error("httpFileServerGetHeader_v1 解析出错");
                    error = true;
                }
            }
        }
        free(oDataChunk.memory);
        free(address);
        if (error)
        {
            return -1;
        }
    }
    else
    {
        dzlog_error("httpFileServerGetHeader_v1 url is null");
        return -1;
    }
    return 0;
}
/**
 * @description: http上传文件
 * @param {char} *filename
 * @return {*} -2找不到文件，-1其他错误 -3文件大小为0
 */
int httpFileServerUploadFile(char *filename, ST_IOTA_AUTH_RES *header, ST_IOTA_FILE_RES *res, ProgressInfo *info)
{
    // 创建一个 curl_httppost 结构体，用于存储 form-data 数据
    struct curl_httppost *body = NULL;
    struct curl_httppost *lastptr = NULL;

    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;

    long size = calculate_file_size(filename);

    if (size < 0)
    {
        dzlog_error("File does not exist or cannot be accessed.\n");
        free(oDataChunk.memory);
        return -2;
    }
    else if (size == 0)
    {
        dzlog_error("File size is 0\n");
        free(oDataChunk.memory);
        return -3;
    }

    bool error = false;
    if (url)
    {
        int index = -1;
        for (int i = 0; i < 20; i++)
        {
            if (progress_info[i] == NULL)
            {
                index = i;
                progress_info[i] = info;
                break;
            }
        }

        if (index == -1)
        {
            dzlog_error("progress_info is full");
            free(oDataChunk.memory);
            return -4;
        }

        char *header1 = malloc(strlen(header->accessKey) + strlen(header->timestamp) + strlen(header->sign) + strlen(header->nonce) + 100);
        char *address = malloc(strlen(url) + strlen("/iot-file-service/upload\0") + 1);
        sprintf(address, "%s%s", url, "/iot-file-service/upload\0");
        dzlog_debug("httpFileServerUploadFile url:%s , address:%s", url, address);
        // 设置头部插入
        sprintf(header1, "accessKey:%s|timestamp:%s|sign:%s|nonce:%s\0", header->accessKey, header->timestamp, header->sign, header->nonce);
        // 添加一个字段（例如，文件字段）
        curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "file", CURLFORM_FILE, filename, CURLFORM_END);
        CURLcode code = curl_post_req(address, body, &oDataChunk, header1, 3, true, header->accessKey, false, info);
        if (code != CURLE_OK)
        {
            dzlog_error("请求出错：%s", curl_easy_strerror(code));
            error = true;
        }
        else
        {
            if (oDataChunk.rescode != 200)
            {
                dzlog_error("返回出错：%s", oDataChunk.memory);
                error = true;
            }
            else
            {
                if (JsonToStruct_FileRes(oDataChunk.memory, res))
                {
                    dzlog_debug("解析成功");
                }
                else
                {
                    dzlog_error("解析出错");
                    error = true;
                }
            }
        }
        free(oDataChunk.memory);
        free(header1);
        free(address);

        curl_formfree(body);
        progress_info[index] = NULL;
        if (error)
        {
            return -1;
        }
    }
    else
    {
        dzlog_error("url is null");
        return -1;
    }
    return 0;
}

int httpFileServerUploadFile_v1(char *filename, ST_IOTA_AUTH_RES_V1 *header, ST_IOTA_FILE_RES *res, ProgressInfo *info)
{
    // 创建一个 curl_httppost 结构体，用于存储 form-data 数据
    // struct curl_httppost *body = NULL;
    // struct curl_httppost *lastptr = NULL;

    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;

    long size = calculate_file_size(filename);

    if (size < 0)
    {
        dzlog_error("File does not exist or cannot be accessed.\n");
        free(oDataChunk.memory);
        return -2;
    }
    else if (size == 0)
    {
        dzlog_error("File size is 0\n");
        free(oDataChunk.memory);
        return -3;
    }

    int error = -1;
    if (url)
    {
        int index = -1;
        for (int i = 0; i < 20; i++)
        {
            if (progress_info[i] == NULL)
            {
                index = i;
                progress_info[i] = info;
                break;
            }
        }
        dzlog_debug("file_name = %s\n", filename);
        char *header1 = malloc(strlen(header->timestamp) + strlen(header->token) + strlen(header->nonce) + 100);
        char *address = malloc(strlen(url) + strlen("/iot-file-service/upload\0") + 1);
        sprintf(address, "%s%s", url, "/iot-file-service/upload\0");
        dzlog_debug("httpFileServerUploadFile url:%s , address:%s", url, address);
        // 设置头部插入
        sprintf(header1, "timestamp:%s|sign:%s|nonce:%s\0", header->timestamp, header->token, header->nonce);
        // 添加一个字段（例如，文件字段）
        // curl_formadd(&body, &lastptr, CURLFORM_COPYNAME, "file", CURLFORM_FILE, filename, CURLFORM_END);
        dzlog_info("httpFileServerUploadFile_v1 header->token[%s]", header->token);
        CURLcode code = curl_post_req(address, filename, &oDataChunk, header1, 4, true, header->token, false, info);
        if (code != CURLE_OK)
        {
            dzlog_error("httpFileServerUploadFile 1curl_post_req address:%s error: %s", address, curl_easy_strerror(code));
            error = -1; // 这里是接口请求失败或者超时
        }
        else
        {
            if (oDataChunk.rescode != 200)
            {
                dzlog_error("httpFileServerUploadFile 2curl_post_req address :%s error:%s", address, oDataChunk.memory);
                error = -2;
            }
            else
            {
                dzlog_debug("httpFileServerUploadFile_v1 resbody = %s", oDataChunk.memory);
                if (JsonToStruct_FileRes(oDataChunk.memory, res))
                {
                    dzlog_debug("httpFileServerUploadFile curl_post_req Parsed successfully");
                    error = 0;
                }
                else
                {
                    dzlog_error("httpFileServerUploadFile curl_post_req Parsing error");
                    error = -4;
                }
            }
        }
        free(oDataChunk.memory);
        free(header1);
        free(address);
        progress_info[index] = NULL;
        // curl_formfree(body);
    }
    else
    {
        dzlog_error("url is null");
        return -1;
    }
    return error;
}

/**
 * @description: 文件确认
 * @param {ST_IOTA_FILE_RES} *res
 * @return {*}
 */
int httpFileServerUploadFileReturn(char *res, char *access_token)
{
    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;
    bool error = false;
    if (url)
    {
        char *address = malloc(strlen(url) + strlen("/api/logs/UploadFileApiService/saveUploadedFile\0") + 1);
        sprintf(address, "%s%s", url, "/api/logs/UploadFileApiService/saveUploadedFile\0");

        dzlog_debug("url = %s", address);
        CURLcode code = curl_post_req(address, res, &oDataChunk, NULL, 2, true, access_token, false, NULL);
        if (code != CURLE_OK)
        {
            dzlog_error("请求出错：%s", curl_easy_strerror(code));
            error = true;
        }
        else
        {
            if (oDataChunk.rescode != 200)
            {
                dzlog_error("返回出错：%s", oDataChunk.memory);
                error = true;
            }
        }
        free(oDataChunk.memory);
        free(address);
        if (error)
        {
            return -1;
        }
    }
    else
    {
        dzlog_error("url is null");
        free(oDataChunk.memory);
        return -1;
    }
    return 0;
}

/**
 * @description: 文件下载
 * @param {ST_IOTA_AUTH_RES} *header 令牌
 * @param {char} *fileId 文件ID
 * @param {char} *filename 文件名称(全文件路径)
 * @return {*}
 */
int httpFileServerDownload(ST_IOTA_AUTH_RES *header, char *fileId, char *filename, char *lurl, ProgressInfo *info)
{
    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;
    bool error = false;
    if (url)
    {
        int index = -1;
        for (int i = 0; i < 20; i++)
        {
            if (progress_info[i] == NULL)
            {
                index = i;
                progress_info[i] = info;
                break;
            }
        }
        char *address = NULL;
        if (lurl != NULL)
        {
            address = malloc(strlen(lurl) + strlen("?fileid=") + 40);
            sprintf(address, "%s?fileId=%s", lurl, fileId);
        }
        else
        {
            address = malloc(strlen(url) + strlen("/iot-file-service/download?fileId=") + strlen(fileId) + 1);
            sprintf(address, "%s/iot-file-service/download?fileId=%s", url, fileId);
        }

        dzlog_debug("url = %s", address);

        char *header1 = malloc(strlen(header->accessKey) + strlen(header->timestamp) + strlen(header->sign) + strlen(header->nonce) + 100);
        sprintf(header1, "accessKey:%s|timestamp:%s|sign:%s|nonce:%s\0", header->accessKey, header->timestamp, header->sign, header->nonce);

        int code = curl_get_req(address, &oDataChunk, header1, true, filename, false, true, NULL, info);
        if (code != CURLE_OK)
        {
            dzlog_error("请求出错：%s", curl_easy_strerror(code));
            error = true;
        }
        else
        {
            if (oDataChunk.rescode != 200)
            {
                dzlog_error("返回出错：%s", oDataChunk.memory);
                error = true;
            }
            free(oDataChunk.memory);
        }
        free(header1);
        free(address);
        progress_info[index] = NULL;
        if (error)
        {
            return -1;
        }
    }
    else
    {
        dzlog_error("url is null");
        return -1;
    }
    return 0;
}

int httpFileServerDownload_v1bak(ST_IOTA_AUTH_RES_V1 *header, char *fileId, char *filename, char *lurl, ProgressInfo *info)
{
    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;
    bool error = false;
    int ret = 0;

    dzlog_debug("httpFileServerDownload_v1 token[%s] nonce[%s]", header->token, header->nonce);

    if (url)
    {
        int index = -1;
        for (int i = 0; i < 20; i++)
        {
            if (progress_info[i] == NULL)
            {
                index = i;
                progress_info[i] = info;
                break;
            }
        }
        char address[1024];

        // 从全路径文件名中提取文件名
        char *file = strrchr(filename, '/');
        if (file)
        {
            file++;
        }
        else
        {
            file = filename;
        }

        char *file_name = curl_easy_escape(NULL, file, strlen(file));

        if (lurl != NULL)
        {
            // address = malloc(strlen(lurl) + strlen("?fileid=") + 40);
            sprintf(address, "%s?fileId=%s", lurl, fileId);
        }
        else
        {

            // address = malloc(strlen(url) + strlen("/iot-file-service/download?fileId=") + strlen(fileId) + strlen("&filename=") + strlen(info.filename + 1));
            sprintf(address, "%s/iot-file-service/download?fileId=%s&fileName=%s&sha256=%s&fileSize=%ld", url, fileId, file_name, info->md5, info->filesize);
        }

        dzlog_debug("url = %s", address);
        // printf("url = %s sizeof = %d length = %d\n", address, strlen(url) + strlen("/iot-file-service/download?fileId=") + strlen(fileId) + strlen("&filename=") + strlen(info.filename + 1), strlen(address));

        char *header1 = malloc(strlen(header->timestamp) + strlen(header->token) + strlen(header->nonce) + 100);
        sprintf(header1, "timestamp:%s|sign:%s|nonce:%s\0", header->timestamp, header->token, header->nonce);
        dzlog_debug("httpFileServerDownload_v1 header1[%s]", header1);
        int code = curl_get_req(address, &oDataChunk, header1, true, filename, false, true, header->token, info);
        if (code != CURLE_OK && code > 0)
        {
            dzlog_error("httpFileServerDownload_v1 error %d %s", code, curl_easy_strerror(code));
            printf("httpFileServerDownload_v1 error %s\n", curl_easy_strerror(code));
            error = true;
        }
        else if (code < 0)
        {
            dzlog_error("httpFileServerDownload_v1 file error %d %s", code, oDataChunk.memory);
            printf("httpFileServerDownload_v1 file error %s\n", oDataChunk.memory);
            ret = -4;
        }
        else
        {
            // if (oDataChunk.rescode != 200)
            if (oDataChunk.rescode >= 400)
            {
                printf("download error2\n");
                dzlog_error("返回出错：%s", oDataChunk.memory);
                printf("返回出错：%s\n", oDataChunk.memory);
                error = true;
            }
            else
            {
                dzlog_debug("httpFileServerDownload_v1 reMsg[%s]", oDataChunk.memory);
                printf("httpFileServerDownload_v1 reMsg[%s]\n", oDataChunk.memory);
                // cJSON* json = cJSON_Parse(oDataChunk.memory);
                // if (json)
                //{
                //     dzlog_error("httpFileServerDownload_v1 reMsg[%s]", oDataChunk.memory);
                //     error = true;
                //     cJSON_Delete(json);
                // }
            }
            free(oDataChunk.memory);
        }
        free(header1);
        curl_free(file_name);
        progress_info[index] = NULL;
        if (error)
        {
            if (code == CURLE_WRITE_ERROR)
            {
                ret = -4;
            }
            else
            {
                ret = -2;
            }
        }
    }
    else
    {
        printf("url is null\n");
        dzlog_error("url is null");
        ret = -1;
    }
    return ret;
}

int httpFileServerDownload_v1(ST_IOTA_AUTH_RES_V1 *header, char *fileId, char *filename, char *lurl, ProgressInfo *info)
{
    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;
    bool error = false;
    int ret = 0;

    if (header)
        dzlog_debug("httpFileServerDownload_v1 token[%s] nonce[%s]", header->token, header->nonce);

    if (url)
    {
        int index = -1;
        for (int i = 0; i < 20; i++)
        {
            if (progress_info[i] == NULL)
            {
                index = i;
                progress_info[i] = info;
                break;
            }
        }
        char address[1024];

        // 从全路径文件名中提取文件名
        char *file = strrchr(filename, '/');
        if (file)
        {
            file++;
        }
        else
        {
            file = filename;
        }

        char *file_name = curl_easy_escape(NULL, file, strlen(file));

        if (lurl != NULL)
        {
            // address = malloc(strlen(lurl) + strlen("?fileid=") + 40);
            sprintf(address, "%s?fileId=%s", lurl, fileId);
        }
        else
        {

            // address = malloc(strlen(url) + strlen("/iot-file-service/download?fileId=") + strlen(fileId) + strlen("&filename=") + strlen(info.filename + 1));
#ifndef TEST_LIANTIAO_ALL_PROLOCOL
	    sprintf(address, "%s/iot-file-service/download?fileId=%s&fileName=%s&sha256=%s&fileSize=%ld", url, fileId, file_name, info->md5, info->filesize);
#else
	    sprintf(address, "%s/%s",url,file_name);
#endif
	}

        dzlog_debug("url = %s", address);
        // printf("url = %s sizeof = %d length = %d\n", address, strlen(url) + strlen("/iot-file-service/download?fileId=") + strlen(fileId) + strlen("&filename=") + strlen(info.filename + 1), strlen(address));
        char *header1 = NULL;
        int code = -1;

        if (header)
        {
            header1 = malloc(strlen(header->timestamp) + strlen(header->token) + strlen(header->nonce) + 100);
            sprintf(header1, "timestamp:%s|sign:%s|nonce:%s\0", header->timestamp, header->token, header->nonce);
            dzlog_debug("httpFileServerDownload_v1 header1[%s]", header1);
            code = curl_get_req(address, &oDataChunk, header1, true, filename, false, true, header->token, info);
        }
        else
        {
            code = curl_get_req(address, &oDataChunk, NULL, true, filename, false, false, NULL, info);
        }
        if (code != CURLE_OK && code > 0)
        {
            dzlog_error("httpFileServerDownload_v1 error %d %s", code, curl_easy_strerror(code));
            printf("httpFileServerDownload_v1 error %s\n", curl_easy_strerror(code));
            error = true;
        }
        else if (code < 0)
        {
            dzlog_error("httpFileServerDownload_v1 file error code[%d]", code);
            //printf("httpFileServerDownload_v1 file error %s\n", oDataChunk.memory);
            ret = -3;
        }
        else
        {
            // if (oDataChunk.rescode != 200)
            if (oDataChunk.rescode >= 400)
            {
                printf("download error2\n");
                dzlog_error("返回出错：%s", oDataChunk.memory);
                printf("返回出错：%s\n", oDataChunk.memory);
                error = true;
            }
            else
            {
                dzlog_debug("httpFileServerDownload_v1 succ");
                //dzlog_debug("httpFileServerDownload_v1 reMsg[%s]", oDataChunk.memory);
                //printf("httpFileServerDownload_v1 reMsg[%s]\n", oDataChunk.memory);
                // cJSON* json = cJSON_Parse(oDataChunk.memory);
                // if (json)
                //{
                //     dzlog_error("httpFileServerDownload_v1 reMsg[%s]", oDataChunk.memory);
                //     error = true;
                //     cJSON_Delete(json);
                // }
            }
            free(oDataChunk.memory);
        }
        if (header)
            free(header1);
        curl_free(file_name);
        progress_info[index] = NULL;
        if (error)
        {
            if (code == CURLE_WRITE_ERROR)
            {
                ret = -4;
                dzlog_error("httpFileServerDownload_v1 error CURLE_WRITE_ERROR");
            }
            else
            {
                ret = -2;
            }
        }
    }
    else
    {
        printf("url is null\n");
        dzlog_error("url is null");
        ret = -1;
    }
    return ret;
}

int httpFileServerDownload_v2(ST_IOTA_AUTH_RES_V1 *header, char *fileId, char *filename, char *lurl, char *token, ProgressInfo *info)
{
    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;
    bool error = false;
    int ret = 0;

    dzlog_debug("httpFileServerDownload_v1 token[%s] nonce[%s]", header->token, header->nonce);

    if (url)
    {
        int index = -1;
        for (int i = 0; i < 20; i++)
        {
            if (progress_info[i] == NULL)
            {
                index = i;
                progress_info[i] = info;
                break;
            }
        }
        char *address = NULL;
        if (lurl != NULL)
        {
            address = malloc(strlen(lurl) + strlen("?fileid=") + 40);
            sprintf(address, "%s?fileId=%s", lurl, fileId);
        }
        else
        {
            address = malloc(strlen(url) + strlen("/iot-file-service/download?fileId=") + strlen(fileId) + strlen("&filename=") + strlen(info->filename + 1));
            sprintf(address, "%s/iot-file-service/download?fileId=%s&fileName=%s", url, fileId, info->filename);
        }

        dzlog_debug("url = %s", address);

        char *header1 = malloc(strlen(header->timestamp) + strlen(header->token) + strlen(header->nonce) + 100);
        sprintf(header1, "timestamp:%s|sign:%s|nonce:%s\0", header->timestamp, header->token, header->nonce);
        dzlog_debug("httpFileServerDownload_v1 header1[%s]", header1);
        CURLcode code = curl_get_req(address, &oDataChunk, header1, true, filename, false, true, token, info);
        if (code != CURLE_OK && code > 0)
        {
            dzlog_error("httpFileServerDownload_v1 error %d %s", code, curl_easy_strerror(code));
            printf("httpFileServerDownload_v1 error %s\n", curl_easy_strerror(code));
            error = true;
        }
        else if (code < 0)
        {
            dzlog_error("httpFileServerDownload_v1 file error %d %s", code, oDataChunk.memory);
            printf("httpFileServerDownload_v1 file error %s\n", oDataChunk.memory);
            ret = -4;
        }
        else
        {
            if (oDataChunk.rescode != 200)
            {
                printf("download error2\n");
                dzlog_error("返回出错：%s", oDataChunk.memory);
                printf("返回出错：%s\n", oDataChunk.memory);
                error = true;
            }
            else
            {
                dzlog_debug("httpFileServerDownload_v1 reMsg[%s]", oDataChunk.memory);
                printf("httpFileServerDownload_v1 reMsg[%s]\n", oDataChunk.memory);
                // cJSON* json = cJSON_Parse(oDataChunk.memory);
                // if (json)
                //{
                //     dzlog_error("httpFileServerDownload_v1 reMsg[%s]", oDataChunk.memory);
                //     error = true;
                //     cJSON_Delete(json);
                // }
            }
            free(oDataChunk.memory);
        }
        free(header1);
        free(address);
        progress_info[index] = NULL;
        if (error)
        {
            ret = -2;
        }
    }
    else
    {
        printf("url is null\n");
        dzlog_error("url is null");
        ret = -1;
    }
    return ret;
}


/*
功能：post http数据
参数：url：请求字符串，比如：http://127.0.0.1:8080/api/Accounts/Login
postParams：请求附带的参数，body，比如uername=admin&password=123
listRequestHeader：请求头数据列表。字符串逻辑，通过|分割 如果没有则为NULL
bResponseIsWithHeaderData：bool类型，表示响应体中是否包含应答头数据。true，包含，false，不包含。如果包含的话，应答数据中包含Content-Type，Server等信息。
content_type选择发送类型，目前支持json（1）和x-www-form-urlencoded（2）和multipart/form-data（3）
auth 是否需要令牌，为ture则传递access_token
返回值：CURLcode
*/

CURLcode curl_post_req_sd(char *url, char *postParams, MemoryStruct *response, char *listRequestHeader, int content_type, bool auth, char *access_token, bool bResponseIsWithHeaderData, ProgressInfo *info)
{

    // init curl
    CURL *curl = curl_easy_init();
    // res code
    CURLcode res;
    // nConnectTimeout：连接超时时间，单位为秒；
    // nTimeout：读写数据超时时间，单位为秒
    int nConnectTimeout = 60;
    int nTimeout = 60 * 10;

    char *body = postParams;
    curl_mime *mime = NULL;
    curl_mimepart *part = NULL;

    if (curl)
    {
        // set params
        curl_easy_setopt(curl, CURLOPT_URL, url); // url
        // curl_easy_setopt(m_curl, CURLOPT_PORT, 8089);    //port
        // curl_easy_setopt(curl, CURLOPT_POST, 1L); // post reqest
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
        //  构建HTTP报文头
        struct curl_slist *headers = NULL;
        if (listRequestHeader)
        {
            char *result = NULL;
            result = strtok(listRequestHeader, "|");
            while (result != NULL)
            {
                headers = curl_slist_append(headers, result);
                result = strtok(NULL, "|");
            }
        }

        switch (content_type)
        {
        case 1:
            headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
            break;
        case 2:
            headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
            break;
        case 3:
            headers = curl_slist_append(headers, "Content-Type: multipart/form-data");
            break;
        case 4:

            break;
        default:
            dzlog_debug("content_type is NULL");
            break;
        }

        if (headers != NULL)
        {
            dzlog_debug("headers is not null");
            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // 设置http请求头信息
        }

        if (auth)
        {
            curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, access_token);
            curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
        }

        if (content_type != 3 && content_type != 4 && body != NULL)
        {
            dzlog_debug("body = %s", body);
            curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
        }
        else if (content_type == 3 && body != NULL)
        {
            dzlog_debug("Content-Type: multipart/form-data");
            curl_easy_setopt(curl, CURLOPT_HTTPPOST, (struct curl_httppost *)body);
        }
        else if (content_type == 4 && body != NULL)
        {
            dzlog_debug("Content-Type: multipart/form-data");
            mime = curl_mime_init(curl);
            part = curl_mime_addpart(mime);
            curl_mime_name(part, "file");
            curl_mime_filedata(part, body);
            curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);

            if (info != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressCallback);
                curl_easy_setopt(curl, CURLOPT_XFERINFODATA, info);
                // 打开进度条显示
                curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
            }
        }
        // 不走https
//        if (HTTP_SSL == 0)
        {
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
            curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
        }

        curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslFunctionDefault);
        // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (int *)strlen(postParams)); // size //设置报文长度
        //  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);                     // if want to use https
        //  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);                     // set peer and host verify false
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);        // 执行可以显示执行过程日志
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 返回的头部中有Location(一般直接请求的url没找到)，则继续请求Location对应的数据 服务重定向
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // 控制是否允许通过信号进行中断操作现在设置为不允许

        if (bResponseIsWithHeaderData)
        {
            
            curl_easy_setopt(curl, CURLOPT_HEADER, 1); // 响应体中是否包含了头信息，比如Content-Type:application/json;charset=UTF-8
        }

        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, nConnectTimeout);
        curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, nTimeout);
        curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L); // 如果速度低于1字节/秒则超时

        // start request
        res = curl_easy_perform(curl);

        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response->rescode);
        dzlog_debug("res = %d", res);
        // 清理资源
        curl_easy_cleanup(curl);
        if (headers != NULL)
        {
            curl_slist_free_all(headers); // free the list again
        }
        if (mime != NULL)
        {
            curl_mime_free(mime);
        }
    }
    else
    {
        dzlog_error("curl_easy_init error");
        return -1;
    }
    // release curl

    return res;
}

CURLcode curl_post_req(char *url, char *postParams, MemoryStruct *response, char *listRequestHeader, int content_type, bool auth, char *access_token, bool bResponseIsWithHeaderData, ProgressInfo *info)
{
    // init curl
    CURL *curl = curl_easy_init();
    // res code
    CURLcode res;
    // nConnectTimeout：连接超时时间，单位为秒；
    // nTimeout：读写数据超时时间，单位为秒
    int nConnectTimeout = 60;
    int nTimeout = 60 * 10;

    char *body = postParams;
    curl_mime *mime = NULL;
    curl_mimepart *part = NULL;

    if (curl)
    {
        // char* ascii_url = convert_to_ascii(url, 1);
        // set params
        curl_easy_setopt(curl, CURLOPT_URL, url); // url
        // curl_easy_setopt(m_curl, CURLOPT_PORT, 8089);    //port
        // curl_easy_setopt(curl, CURLOPT_POST, 1L); // post reqest
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
        //  构建HTTP报文头
        struct curl_slist *headers = NULL;
        if (listRequestHeader)
        {
            char *result = NULL;
            result = strtok(listRequestHeader, "|");
            while (result != NULL)
            {
                headers = curl_slist_append(headers, result);
                result = strtok(NULL, "|");
            }
        }

        switch (content_type)
        {
        case 1:
            headers = curl_slist_append(headers, "Content-Type:application/x-www-form-urlencoded");
            break;
        case 2:
            headers = curl_slist_append(headers, "Content-Type:application/json;charset=UTF-8");
            break;
        case 3:
            headers = curl_slist_append(headers, "Content-Type: multipart/form-data");
            break;
        case 4:

            break;
        default:
            dzlog_debug("content_type is NULL");
            break;
        }

        if (headers != NULL)
        {
            dzlog_debug("headers is not null");
            curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // 设置http请求头信息
        }

        if (auth)
        {
            curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, access_token);
            curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
        }

        if (content_type != 3 && content_type != 4 && body != NULL)
        {
            dzlog_debug("body = %s", body);
            curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body);
        }
        else if (content_type == 3 && body != NULL)
        {
            dzlog_debug("Content-Type:3 multipart/form-data");
            curl_easy_setopt(curl, CURLOPT_HTTPPOST, (struct curl_httppost *)body);
        }
        else if (content_type == 4 && body != NULL)
        {
            dzlog_debug("Content-Type:4 multipart/form-data");
            mime = curl_mime_init(curl);
            part = curl_mime_addpart(mime);
            curl_mime_name(part, "file");
            curl_mime_filedata(part, body);
            curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);

            if (info != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressCallback);
                curl_easy_setopt(curl, CURLOPT_XFERINFODATA, info);
                // 打开进度条显示
                curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
            }
        }
        // 不走https
        if (HTTP_SSL == 0)
        {
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
            curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "https");
        }
        // 走国密双向ssl加密
        else if (HTTP_SSL == 2)
        {
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);

            // 指定为TLCP模式
            curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLCPv1_1);
            // 设置SSL验证函数
            // curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslCtxCallback);

            // 查openssl源码无法使用pem格式的证书兼容，强行指定加密格式为pem
            // 设置ca证书

            curl_easy_setopt(curl, CURLOPT_CAINFO, CLIENT_CA_CERT_PATH);
            // 设置加密证书
            curl_easy_setopt(curl, CURLOPT_SSLDCERT, CLIENT_ENC_CERT_PATH);
            curl_easy_setopt(curl, CURLOPT_SSLDCERTTYPE, "PEM");
            // 设置加密证书秘钥
            curl_easy_setopt(curl, CURLOPT_SSLDKEY, CLIENT_ENC_KEY_PATH);
            curl_easy_setopt(curl, CURLOPT_SSLDKEYTYPE, "PEM");

            // 设置客户端证书文件路径
            curl_easy_setopt(curl, CURLOPT_SSLCERT, CLIENT_SIGN_CERT_PATH);
            // 设置证书类型
            curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
            // 设置客户端私钥文件路径
            curl_easy_setopt(curl, CURLOPT_SSLKEY, CLIENT_SIGN_KEY_PATH);
            // 设置秘钥类型
            curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
            // 设置加密算法列表
            // curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, "ECC-SM4-SM3:ECDHE-SM4-SM3");
            curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, "ECC-SM2-SM3:ECDHE-SM2-SM3");

        }

        // curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (int *)strlen(postParams)); // size //设置报文长度
        //  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);                     // if want to use https
        //  curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);                     // set peer and host verify false
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);        // 执行可以显示执行过程日志
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 返回的头部中有Location(一般直接请求的url没找到)，则继续请求Location对应的数据 服务重定向
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response);
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1L); // 控制是否允许通过信号进行中断操作现在设置为不允许
        if (bResponseIsWithHeaderData)
        {
            curl_easy_setopt(curl, CURLOPT_HEADER, 1); // 响应体中是否包含了头信息，比如Content-Type:application/json;charset=UTF-8
        }
        curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, nConnectTimeout);
        curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, nTimeout);
        curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L); // 如果速度低于1字节/秒则超时
        // start request
        res = curl_easy_perform(curl);
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response->rescode);
        dzlog_debug("res = %d", res);
        // 清理资源
        curl_easy_cleanup(curl);
        if (headers != NULL)
        {
            curl_slist_free_all(headers); // free the list again
        }
        if (mime != NULL)
        {
            curl_mime_free(mime);
        }
    }
    else
    {
        dzlog_error("curl_easy_init error");
        return -1;
    }
    // release curl

    return res;
}

// post回调函数，返回结构体
size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
    size_t totalSize = size * nmemb;
    struct MemoryStruct *mem = (struct MemoryStruct *)userp;
    // 动态分配内存来存储响应数据
    mem->memory = realloc(mem->memory, mem->size + totalSize + 1);
    if (mem->memory == NULL)
    {
        dzlog_error("Failed to allocate memory for response data.");
        return 0;
    }
    memcpy(&(mem->memory[mem->size]), contents, totalSize);
    mem->size += totalSize;
    mem->memory[mem->size] = '\0'; // 添加字符串结束符
    return totalSize;

    // size_t realsize = size * nmemb;
    // char** mem = (char**)userp;

    //*mem = realloc(*mem, (*mem ? strlen(*mem) : 0) + realsize + 1);
    // if (*mem == NULL) {
    //    // 处理内存分配失败的情况
    //    fprintf(stderr, "Failed to allocate memory\n");
    //    return 0;
    //}

    // memcpy(&((*mem)[strlen(*mem)]), contents, realsize);
    //(*mem)[strlen(*mem) + realsize] = '\0';

    // return realsize;
}

/*
功能：get http数据
参数：url：请求字符串。如果请求带参数数据，直接拼凑到url后面；比如：http://127.0.0.1:8080/api/Accounts/Login?uername=admin&password=123
listRequestHeader：请求头数据列表。和post请求一样
isFile是否为文件，是文件则下载文件
filename文件名称 如果为文件，不能为空
bResponseIsWithHeaderData返回是否带header
auth   是否自动登录
access_token 登录token
downNo对应downloadinfo元素对应文件信息 为负数则跳过显示进度
返回值：CURLcode
*/

CURLcode curl_get_req_sd(char *url, MemoryStruct *response, char *listRequestHeader, bool isFile, char *filename, bool bResponseIsWithHeaderData, bool auth, char *access_token, int downNo, ProgressInfo *info)
{
    // init curl
    dzlog_debug("curl_get_req_sd start access_token:%s", access_token);
    dzlog_debug("curl_get_req_sd curl_get_req[%s]", listRequestHeader);
    CURL *curl = curl_easy_init();
    int nConnectTimeout = 60;
    int nTimeout = 60 * 10;
    // res code
    int res = -1;
    FILE *file = NULL;
    if (curl)
    {
        // set params
        curl_easy_setopt(curl, CURLOPT_URL, url); // url
        // curl_easy_setopt(m_curl, CURLOPT_PORT, 8089);    //port
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); // get reqest
        dzlog_debug("curl_get_req start 11");
        // 构建HTTP报文头
        struct curl_slist *headers = NULL;
        if (listRequestHeader != NULL)
        {
            dzlog_debug("curl_get_req start 22");
            char *result = NULL;
            result = strtok(listRequestHeader, "|");
            while (result != NULL)
            {
                headers = curl_slist_append(headers, result);
                result = strtok(NULL, "|");
            }
            if (headers != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // 设置http请求头信息
            }
        }
        dzlog_debug("curl_get_req start 33");
//        if (HTTP_SSL == 0)
        {
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        }

        curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslFunctionDefault);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
        dzlog_debug("curl_get_req start 44");
        if (isFile)
        {
            file = fopen(filename, "ab");

            if (file)
            {

 //               dzlog_info("filename = %s size = %d", filename, info->filesize);
                dzlog_info("filename = %s", filename);
                fseek(file, 0, SEEK_END);
                long long resume_position = ftell(file); // 获取当前文件大小

                if (resume_position < 1048576) // 1M
                {
                    file = freopen(filename, "wb", file);
                    if (!file)
                    {
                        dzlog_error("file wb open error filename:%s", filename);
                        return -1;
                    }
                    resume_position = 0;
                }
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFileCallback);
                curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);

                if (info != NULL)
                {
                    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, info);
                    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressCallback);
                    // 打开进度条显示
                    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
                }

                // 断点续传

                curl_easy_setopt(curl, CURLOPT_RESUME_FROM, resume_position);
            }
            else
            {
                dzlog_error("file open ab+ error filename:%s", filename);
                return -1;
            }
            // 文件下载设置为默认
            curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, nConnectTimeout);
        }
        else
        {
            dzlog_debug("curl_get_req start 66");
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response);
        }
        if (auth)
        {
            dzlog_debug("curl_get_req start 77");
            curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, access_token);
            curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
        }
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 服务重定向
        if (bResponseIsWithHeaderData)
        {
            dzlog_debug("curl_get_req start 88");
            curl_easy_setopt(curl, CURLOPT_HEADER, 1); // 响应体中是否包含了头信息，比如Content-Type:application/json;charset=UTF-8
        }

        // start request
        res = (int)curl_easy_perform(curl);
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response->rescode);

        dzlog_debug("curl_get_req start 9922 res[%d]", res);
        // 查看文件返回格式
        char *content_type;
        curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type);
        dzlog_debug("curl_get_req start 992 content_type[%s]", content_type);
        if (content_type != NULL && strlen(content_type) != 0)
        {
            if (strncmp(content_type, "application/json", 16) == 0 && isFile)
            {
                dzlog_debug("curl_get_req type = %s", content_type);
                res = -1;
            }
        }
        // else
        //{
        //     res = -1;
        // }

        dzlog_debug("curl_get_req start 993");

        if (headers != NULL)
        {
            curl_slist_free_all(headers); // free the list again
        }
        dzlog_debug("curl_get_req start 1010");
        // release curl
        curl_easy_cleanup(curl);
    }

    if (isFile)
    {
        if (file)
        {
            fclose(file);
        }
    }
    dzlog_debug("curl_get_req start 1111");
    return res;
}

int curl_get_req(char *url, MemoryStruct *response, char *listRequestHeader, bool isFile, char *filename, bool bResponseIsWithHeaderData, bool auth, char *access_token, ProgressInfo *info)
{
    dzlog_debug("curl_get_req curl_get_req[%s]", listRequestHeader);
    CURL *curl = curl_easy_init();
    int nConnectTimeout = 60;
    int nTimeout = 60 * 10;
    // res code
    int res = -1;
    FILE *file = NULL;
    if (curl)
    {
        // char* ascii_url = convert_to_ascii(url, 1);

        // set params
        curl_easy_setopt(curl, CURLOPT_URL, url); // url
        // curl_easy_setopt(m_curl, CURLOPT_PORT, 8089);    //port
        curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "GET"); // get reqest
        dzlog_debug("curl_get_req start 11");
        // 构建HTTP报文头
        struct curl_slist *headers = NULL;
        if (listRequestHeader != NULL)
        {
            dzlog_debug("curl_get_req start 22");
            char *result = NULL;
            result = strtok(listRequestHeader, "|");
            while (result != NULL)
            {
                headers = curl_slist_append(headers, result);
                result = strtok(NULL, "|");
            }
            if (headers != NULL)
            {
                curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); // 设置http请求头信息
            }
        }
        dzlog_debug("curl_get_req start 33");
        if (HTTP_SSL == 0)
        {
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
        }
        // 走国密双向ssl加密
        else if (HTTP_SSL == 2)
        {
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0);
            // 指定为TLCP模式
            curl_easy_setopt(curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLCPv1_1);
            // 设置SSL验证函数
            // curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslCtxCallback);

            // 查openssl源码无法使用pem格式的证书兼容，强行指定加密格式为pem
            // 设置ca证书
            curl_easy_setopt(curl, CURLOPT_CAINFO, CLIENT_CA_CERT_PATH);
            // 设置加密证书
            curl_easy_setopt(curl, CURLOPT_SSLDCERT, CLIENT_ENC_CERT_PATH);
            curl_easy_setopt(curl, CURLOPT_SSLDCERTTYPE, "PEM");
            // 设置加密证书秘钥
            curl_easy_setopt(curl, CURLOPT_SSLDKEY, CLIENT_ENC_KEY_PATH);
            curl_easy_setopt(curl, CURLOPT_SSLDKEYTYPE, "PEM");

            // 设置客户端证书文件路径
            curl_easy_setopt(curl, CURLOPT_SSLCERT, CLIENT_SIGN_CERT_PATH);
            // 设置证书类型
            curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE, "PEM");
            // 设置客户端私钥文件路径
            curl_easy_setopt(curl, CURLOPT_SSLKEY, CLIENT_SIGN_KEY_PATH);
            // 设置秘钥类型
            curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
            // 设置加密算法列表
            curl_easy_setopt(curl, CURLOPT_SSL_CIPHER_LIST, "ECC-SM2-SM3:ECDHE-SM2-SM3");

        }
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
        dzlog_debug("curl_get_req start 44");
        if (isFile)
        {
            file = fopen(filename, "ab");

            if (file)
            {

                dzlog_info("filename = %s size = %d", filename, info->filesize);
                fseek(file, 0, SEEK_END);
                long long resume_position = ftell(file); // 获取当前文件大小

                if (resume_position < 1048576) // 1M
                {
                    file = freopen(filename, "wb", file);
                    if (!file)
                    {
                        dzlog_error("file wb open error filename:%s", filename);
                        return -1;
                    }
                    resume_position = 0;
                }
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeFileCallback);
                curl_easy_setopt(curl, CURLOPT_WRITEDATA, file);

                if (info != NULL)
                {
                    curl_easy_setopt(curl, CURLOPT_XFERINFODATA, info);
                    curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressCallback);
                    // 打开进度条显示
                    curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0L);
                }

                // 断点续传

                curl_easy_setopt(curl, CURLOPT_RESUME_FROM, resume_position);

                curl_easy_setopt(curl, CURLOPT_LOW_SPEED_LIMIT, 1L);
                curl_easy_setopt(curl, CURLOPT_LOW_SPEED_TIME, 60L);
            }
            else
            {
                dzlog_error("file open ab+ error filename:%s", filename);
                return -1;
            }
            // 文件下载设置为默认
            curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, nConnectTimeout);
        }
        else
        {
            dzlog_debug("curl_get_req start 66");
            curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteMemoryCallback);
            curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)response);
        }
        if (auth)
        {
            dzlog_debug("curl_get_req start 77");
            curl_easy_setopt(curl, CURLOPT_XOAUTH2_BEARER, access_token);
            curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BEARER);
        }
        curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
        curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); // 服务重定向
        if (bResponseIsWithHeaderData)
        {
            dzlog_debug("curl_get_req start 88");
            curl_easy_setopt(curl, CURLOPT_HEADER, 1); // 响应体中是否包含了头信息，比如Content-Type:application/json;charset=UTF-8
        }
        dzlog_debug("curl_get_req start 99");
        // start request
        res = (int)curl_easy_perform(curl);
        dzlog_debug("curl_get_req start 9911");
        curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response->rescode);

        dzlog_debug("curl_get_req start 9922 res[%d]", res);
        // 查看文件返回格式
        char *content_type;
        curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &content_type);
        dzlog_debug("curl_get_req start 992 content_type[%s]", content_type);
        if (content_type != NULL && strlen(content_type) != 0)
        {
            if (strncmp(content_type, "application/json", 16) == 0 && isFile)
            {
                dzlog_debug("curl_get_req type = %s", content_type);
                res = -1;
            }
        }
        // else
        //{
        //     res = -1;
        // }

        dzlog_debug("curl_get_req start 993");

        if (headers != NULL)
        {
            curl_slist_free_all(headers); // free the list again
        }
        dzlog_debug("curl_get_req start 1010");
        // release curl
        curl_easy_cleanup(curl);
    }

    if (isFile)
    {
        if (file)
        {
            fclose(file);
        }
    }
    dzlog_debug("curl_get_req start 1111");
    return res;
}

// 回调函数，用于将响应数据写入文件
size_t writeFileCallback(void *contents, size_t size, size_t nmemb, void *userp)
{
    FILE *file = (FILE *)userp;
    size_t written = fwrite(contents, size, nmemb, file);
    if (written < nmemb)
    {
        // 写入失败,可能是磁盘满了
        return 0; // 返回0会让curl终止传输
    }
    return written;
}

// 回调函数，用于处理下载进度
// 下载用不上了，作为上传进度来处理
int progressCallback(void *clientp, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow)
{
    // clientp 是用户自定义的数据指针
    // dltotal 是总下载字节数
    // dlnow 是当前下载的字节数
    // ultotal 是总上传字节数
    // ulnow 是当前上传的字节数

    // 计算下载进度百分比
    // info->downloadProgress = dlnow / dltotal * 100.0;
    int isCompleted = 0;
    ProgressInfo *info = (ProgressInfo *)clientp;
    if (info->action == 1)
    {
        // 下载字节数
        info->Bytes = dlnow;
        // 下载进度百分比
        info->Progress = (double)(dlnow + info->Bytes_bak) / (double)info->filesize * 100;

        //printf("download_info.downloadProgress = %f,dlnow = %ld,filesize = %ld,filename = %s\n", info->Progress, info->Bytes, info->filesize, info->filename);
        //dzlog_debug("progressCallback info->Progress[%f],info->filesize[%d]", info->Progress, info->filesize);
    }
    else
    {
        // 上传字节数
        info->Bytes = ulnow;
        // 上传进度百分比
        info->Progress = (double)ulnow / (double)ultotal * 100;
        // 上传文件字节数
        info->filesize = ultotal;
        //printf("uploadProgress = %f ,ulnow = %ld,ultotal = %ld,filename = %s\n", info->Progress, info->Bytes, info->filesize, info->filename);
    }
    

    //   下载字节数
    // 文件字节数 后端不支持，暂时先不弄
    // info->filesize = dltotal;
    if (info->stop != 0)
    {
        return 1;
    }
    return 0;
}

CURLcode sslCtxCallback(CURL *curl, void *sslctx, void *userptr)
{
    SSL_CTX *ctx = (SSL_CTX *)sslctx;
    bool isError = false;
    int rc = 0;
    // sign cert
    if ((rc = SSL_CTX_use_certificate_file(ctx, CLIENT_SIGN_CERT_PATH, SSL_FILETYPE_PEM)) != 1)
    {
        dzlog_error("SSL_CTX_use_certificate_file load Signature certificate failed");
        isError = true;
        goto error;
    }

    // sign private key
    if ((rc = SSL_CTX_use_PrivateKey_file(ctx, CLIENT_SIGN_KEY_PATH, SSL_FILETYPE_PEM)) != 1)
    {
        dzlog_error("SSL_CTX_use_certificate_file load Signature key failed");
        isError = true;
        goto error;
    }

    if ((rc = SSL_CTX_check_private_key(ctx)) != 1)
    {
        dzlog_error("SSL_CTX_check_private_key check Signature key failed");
        isError = true;
        goto error;
    }

    // encrypt cert
    if ((rc = SSL_CTX_use_enc_certificate_file(ctx, CLIENT_ENC_CERT_PATH, SSL_FILETYPE_PEM)) != 1)
    {
        dzlog_error("SSL_CTX_use_enc_certificate_file load Encrypt certificate failed");
        goto error;
    }

    // encrypt private key
    if ((rc = SSL_CTX_use_enc_PrivateKey_file(ctx, CLIENT_ENC_KEY_PATH, SSL_FILETYPE_PEM)) != 1)
    {
        dzlog_error("SSL_CTX_use_enc_PrivateKey_file load Encrypt Key failed");
        isError = true;
        goto error;
    }

    if ((rc = SSL_CTX_check_enc_private_key(ctx)) != 1)
    {
        dzlog_error("SSL_CTX_check_enc_private_key check Encrypt Key failed");
        isError = true;
        goto error;
    }

    // ca cert
    if ((rc = SSL_CTX_load_verify_locations(ctx, CLIENT_CA_CERT_PATH, NULL)) != 1)
    {
        dzlog_error("CA can't be empty!!!");
        isError = true;
        goto error;
    }

    if ((rc = SSL_CTX_set_cipher_list(ctx, "ECC-SM4-SM3:ECDHE-SM4-SM3")) != 1)
    {
        dzlog_error(" can't find cipher mod");
        isError = true;
        goto error;
    }
    SSL_CTX_set_mode(ctx, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);

error:
    if (isError)
    {
        return CURLE_SSL_ENGINE_SETFAILED;
    }
    return CURLE_OK;
}

int httpSubDeviceAuthReq(char *psContent)
{
    if (g_psSubDeviceAuthUrl == NULL && psContent == NULL)
    {
        dzlog_error("g_psSubDeviceAuthUrl or input is null!");
        return -1;
    }

    MemoryStruct oDataChunk;
    oDataChunk.memory = malloc(1); // 初始化内存
    oDataChunk.size = 0;
    int ret = 0;
    
    dzlog_info("url = %s", g_psSubDeviceAuthUrl);
    CURLcode code = curl_post_req(g_psSubDeviceAuthUrl, psContent, &oDataChunk, NULL, 2, false, NULL, false, NULL);
    if (code != CURLE_OK)
    {
        dzlog_error("req failed:%s", curl_easy_strerror(code));
        ret = -1;
    }
    else
    {
        if (oDataChunk.rescode != 200)
        {
            dzlog_error("http respose failed:%d", oDataChunk.rescode);
            ret = -1;
        }
        else
        {
            dzlog_info("req success:%s", oDataChunk.memory);
            cJSON *json = NULL;
            json = cJSON_Parse(oDataChunk.memory);
            if (json)
            {
                int iCode = -1;
                cJSON *pCode = cJSON_GetObjectItem(json, "code");
                if(pCode != NULL)
                {               
                    if(cJSON_IsString(pCode))
                        iCode = atoi(pCode->valuestring);   
                    else if(cJSON_IsNumber(pCode))
                        iCode =  pCode->valueint;            
                }

                if(iCode != 1)
                {
                    dzlog_error("http auth failed:%s", oDataChunk.memory);
                    ret = -1;
                }
                cJSON_Delete(json);
            }
            else
            {
                dzlog_error("http respose parse failed:%s", oDataChunk.memory);
                ret = -1;
            }
        }
    }

    free(oDataChunk.memory);
    return ret;
}